package com.aabte.commons.encrypt;

import com.aabte.commons.util.ExceptionUtils;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Properties;

/**
 * @author Daniel
 * @version 1.0
 * @date 2020/3/29
 */
@Slf4j
public class EncryptConfig {

  private static final String DEFAULT_CONFIG_PATH = "encrypt";
  private static final String DEFAULT_CONFIG_FILE_NAME = "encrypt.properties";
  private static final String DEFAULT_PRIVATE_KEY_FILE_NAME = "private_key.pem";
  private static final String DEFAULT_PUBLIC_KEY_FILE_NAME = "public_key.pem";

  /** 环境变量名，通过此变量指定相关配置文件所在目录 */
  private static final String ENVIRONMENT_CONFIG_PATH_PROPERTY = "ENCRYPT_CONFIG_PATH";

  /**
   * 配置项：加密实现
   */
  private static final String PROVIDER_NAME_PROPERTY = "provider-name";
  /**
   * 配置项：私钥文件名
   */
  private static final String PROVIDER_PRIVATE_KEY_FILE_NAME = "private-key-file-name";
  /**
   * 配置项：公钥文件名
   */
  private static final String PROVIDER_PUBLIC_KEY_FILE_NAME = "public-key-file-name";

  /** 配置文件目录 */
  private static String configPath = DEFAULT_CONFIG_PATH;

  /**
   * 配置
   */
  private static final Properties PROPERTIES = new Properties();

  private static byte[] privateKeyContent;
  private static byte[] publicKeyContent;

  static {
    loadProp();
    init();
  }

  public static String providerName() {
    return PROPERTIES.getProperty(PROVIDER_NAME_PROPERTY);
  }

  public static Properties properties() {
    Properties properties = new Properties();
    properties.putAll(PROPERTIES);
    return properties;
  }

  public static byte[] privateKeyContent() {
    return privateKeyContent;
  }

  public static byte[] publicKeyContent() {
    return publicKeyContent;
  }

  private static void loadProp() {
    String value = System.getenv(ENVIRONMENT_CONFIG_PATH_PROPERTY);
    if (null != value && value.trim().length() > 0) {
      File file = new File(value, DEFAULT_CONFIG_FILE_NAME);
      try {
        loadPropertiesFile(file);
        configPath = value.trim();
        return;
      } catch (Exception e) {
        // nothing
      }
    }

    File file = new File(DEFAULT_CONFIG_PATH, DEFAULT_CONFIG_FILE_NAME);
    try {
      loadPropertiesFile(file);
    } catch (Exception e) {
      log.warn("load encrypt configuration file failed.");
    }
  }

  private static void loadPropertiesFile(File file) {
    URL resource = AbstractEncryptor.class.getClassLoader().getResource("");
    if (null != resource) {
      String classpath = resource.getPath();
      file = new File(classpath, file.getPath());
      if (file.isFile() && file.canRead()) {
        try (InputStream in = new FileInputStream(file)) {
          PROPERTIES.load(in);
        } catch (IOException e) {
          throw new EncryptException(e);
        }
      } else {
        throw new EncryptException("read encrypt configuration file failed.");
      }
    }
  }

  private static void init() {
    String privateKeyFileName =
        PROPERTIES.getProperty(PROVIDER_PRIVATE_KEY_FILE_NAME, DEFAULT_PRIVATE_KEY_FILE_NAME);
    File privateKeyFile = new File(configPath + File.separatorChar + privateKeyFileName);
    try {
      privateKeyContent = localKeyContent(privateKeyFile);
    } catch (Exception e) {
      privateKeyFile = new File(configPath + File.separatorChar + DEFAULT_PRIVATE_KEY_FILE_NAME);
      try {
        privateKeyContent = localKeyContent(privateKeyFile);
      } catch (Exception ex) {
        log.warn("load private key failed. {}", ExceptionUtils.getErrorStack(e));
      }
    }

    String publicKeyFileName =
        PROPERTIES.getProperty(PROVIDER_PUBLIC_KEY_FILE_NAME, DEFAULT_PUBLIC_KEY_FILE_NAME);
    File publicKeyFile = new File(configPath + File.separatorChar + publicKeyFileName);
    try {
      publicKeyContent = localKeyContent(publicKeyFile);
    } catch (Exception e) {
      publicKeyFile = new File(configPath + File.separatorChar + DEFAULT_PUBLIC_KEY_FILE_NAME);
      try {
        publicKeyContent = localKeyContent(publicKeyFile);
      } catch (Exception ex) {
        log.warn("load public key failed. {}", ExceptionUtils.getErrorStack(e));
      }
    }
  }

  private static byte[] localKeyContent(File keyFile) {
    URL resource = AbstractEncryptor.class.getClassLoader().getResource("");
    if (null != resource) {
      String classPath = resource.getPath();
      keyFile = new File(classPath, keyFile.getPath());
      try (BufferedReader reader = new BufferedReader(new FileReader(keyFile))) {
        StringBuilder sb = new StringBuilder();
        String line;
        while (null != (line = reader.readLine())) {
          if (line.startsWith("-")) {
            continue;
          }
          sb.append(line);
        }
        String keyByBase64 = sb.toString();
        return Base64.getDecoder().decode(keyByBase64.getBytes(StandardCharsets.UTF_8));
      } catch (IOException e) {
        throw new EncryptException(e);
      }
    }
    throw new EncryptException("encrypt key file not found.");
  }
}
